<?php

    /**
     * Url
     *
     * Object that contains url datas, and which provides static method helpers
     *
     * @package      Core
     * @subpackage   Url
     *
     * @license      GNU Lesser General Public Licence see LICENCE-LGPL file or http://www.gnu.org/licenses/lgpl.html
     */
    class Core_Url extends Core_Url_Abstract
    {

    ////////////////////////////////////////////////////////////////////////////
	//                               Constantes                               //
	////////////////////////////////////////////////////////////////////////////

       /**#@+
        * Constant for get() method
        *
        * @var integer
        */
       const STRING     = 0;
       const XMLSTRING  = 1;
       const URL        = 2;
       const URLACTION  = 3;
       /**#@-*/

    ////////////////////////////////////////////////////////////////////////////
	//                                Variables                               //
	////////////////////////////////////////////////////////////////////////////

        /**
         * Singleton Core_Url_Engine object
         *
         * @var Core_Url_Engine
         */
        protected static $_engine;

        /**
         * Script name including its path
         *
         * @var string
         */
        protected $_scriptName;

        /**
         * Path info part of the url
         *
         * @var string
         */
        protected $_pathInfo;

	////////////////////////////////////////////////////////////////////////////
	//                            Getters / Setters                           //
	////////////////////////////////////////////////////////////////////////////

        /**
         * Gets the script name
         *
         * @return      string
         */
        public function getScriptName()
        {
            return $this->_scriptName;
        }

        /**
         * Sets the script name
         *
         * @param       string      $value : parameter value
		 * @throws		Core_ArgumentException
		 * @throws		Core_ArgumentNullException
         * @return      Core_Url
         */
        public function setScriptName($value)
        {
            if ($value === null) {
				throw new Core_ArgumentNullException('value is a null reference');
			}

            if (strlen(trim($value)) == 0) {
				throw new Core_ArgumentException('value is a zero-length string');
			}

            $this->_scriptName = $value;
            return $this;
        }

        /**
         * Gets the path info part of the url
         *
         * @return      string
         */
        public function getPathInfo()
        {
            return $this->_pathInfo;
        }

        /**
         * Sets the path info part of the url
         *
         * @param       string      $value : parameter value
		 * @throws		Core_ArgumentException
		 * @throws		Core_ArgumentNullException
         * @return      Core_Url
         */
        public function setPathInfo($value)
        {
            if ($value === null) {
				throw new Core_ArgumentNullException('value is a null reference');
			}

            if (strlen(trim($value)) == 0) {
				throw new Core_ArgumentException('value is a zero-length string');
			}

            $this->_pathInfo = $value;
            return $this;
        }

	////////////////////////////////////////////////////////////////////////////
	//                             Static Methods                             //
	////////////////////////////////////////////////////////////////////////////

        /**
         * Singleton engine instance
         *
         * @param       boolean     $reset
         * @return      Core_Url_Engine
         * @internal    call with true parameter, to force to re-instancy the engine. usefull for test suite
         */
        public static function getEngine($reset = false)
        {
            $registry = Zend_Registry::getInstance();

            if ($reset) {
                self::$_engine = null;
            }

            if (self::$_engine === null) {
                $className = 'Core_Url_Engine_'. $registry['config']->urlengine->engine;
                self::$_engine = new $className();
            }

            return self::$_engine;
        }

	////////////////////////////////////////////////////////////////////////////
	//                                 Methods                                //
	////////////////////////////////////////////////////////////////////////////

        /**
         * Constructor
         *
         * @param       string      $scriptname : script name
         * @param       array       $params : parameters
         * @param       string      $pathInfo : path info contents
         * @throws		Core_ArgumentNullException
         */
        public function __construct($scriptname = '', array $params = array(), $pathInfo = '')
        {
            if ($scriptname === null) {
				throw new Core_ArgumentNullException('scriptname is a null reference');
			}

            if ($pathInfo === null) {
				throw new Core_ArgumentNullException('pathInfo is a null reference');
			}

            $this->_params      = $params;
            $this->_pathInfo    = $pathInfo;
            $this->_scriptName  = $scriptname;
        }

        /**
         * Destructor
         */
        public function __destruct()
        {
            unset($this);
        }

        /**
         * Converts the url to a string
         *
         * @param       boolean     $forxml : true if some characters should be escaped
         * @return      string
         */
        public function _toString($forxml = false)
        {
            return $this->getPath() . $this->getQuery($forxml);
        }

        /**
         * Gets the path part of the url (scriptName + pathinfo)
         *
         * @return      string
         */
        public function getPath()
        {
            $url = $this->_scriptName;

            if (substr($this->_scriptName, -1) == '/') {
                $url .= ltrim($this->_pathInfo, '/');
            } else {
                $url .= $this->_pathInfo;
            }

            return $url;
        }

        /**
        * Gets the query part of the url
        *
        * @param        boolean     $forxml : true if some characters should be escaped
        * @return       string
        */
       public function getQuery($forxml = false) {
           $url = '';

            if (count($this->_params) > 0) {
                $q = http_build_query($this->_params, '', ($forxml ? '&amp;' : '&'));

                if (strpos($q, '%3A') !== false) {
                    $q = str_replace('%3A', ':', $q);
                }

                $url .= '?' . $q;
            }

           return $url;
       }

	////////////////////////////////////////////////////////////////////////////
	//                              Static methods                            //
	////////////////////////////////////////////////////////////////////////////

        /**
         * Gets current Url
         *
         * @param       boolean     $forxml : if true, escape some characters to include the url into an html/xml document
         * @return      string      the url
         */
        public static function getCurrentUrl($forxml = false)
        {
            if (isset($_SERVER["REQUEST_URI"])) {
               return $_SERVER["REQUEST_URI"];
            }

            static $url = false;
			$coordinator = Core_Controller_Coordinator::getInstance();

            if ($url === false) {
                $url = 'http://'
                        . $_SERVER['HTTP_HOST']
                        . $coordinator->getRequest()->getUrlScript()
                        . $coordinator->getRequest()->getUrlPathInfo()
                        .'?';

                $q = http_build_query($_GET, '', ($forxml ? '&amp;' : '&'));

                if (strpos($q, '%3A') !== false) {
                    $q = str_replace('%3A', ':', $q);
                }

                $url .= '?' . $q;
            }

            return $url;
        }

        /**
         * Gets domain
         *
         * @param       string      $prefix
         * @return      string      the domain
         */
        public static function getDomain($prefix = '.')
        {
            $http_host = explode('.', $_SERVER['HTTP_HOST']);

            if ((count($http_host) == 1) || (count($http_host) == 4)) {
                // The http host is localhost or an ip address
                return $_SERVER['HTTP_HOST'];
            }

            if (count($http_host) == 3) {
                // With www
                array_shift($http_host);
            }

            return $prefix . implode('.', $http_host);
        }

        /**
         * Adds parameters to the given url
         *
         * @param       string      $url : an URL
         * @param       array       $params : some parameters to append to the url
         * @param       boolean     $forxml : if true, escape some characters to include the url into an html/xml document
         * @return      string      the url
         */
        public static function appendToUrlString($url, array $params = array(), $forxml = false)
        {
            $q = http_build_query($params, '', ($forxml ? '&amp;' : '&'));

            if (strpos($q, '%3A') !== false) {
                $q = str_replace('%3A', ':', $q);
            }

            if ((($pos = strpos($url, '?')) !== false) && ($pos !== (strlen($url) - 1))) {
                return $url . ($forxml ? '&amp;' : '&') . $q;
            } else {
                return $url . '?' . $q;
            }
        }

        /**
         * Parses an url
         *
         * @param		string		$scriptNamePath : /path/index.php
         * @param       string      $pathinfo : the path info of the url
         * @param		array		$params : url parameter ($_REQUEST)
		 * @throws		Core_ArgumentException
		 * @throws		Core_ArgumentNullException
         * @return		Core_Url_Action
         */
        public static function parse($scriptNamePath, $pathinfo, array $params)
        {
            if ($scriptNamePath === null) {
				throw new Core_ArgumentNullException('scriptNamePath is a null reference');
			}

            if (strlen(trim($scriptNamePath)) == 0) {
				throw new Core_ArgumentException('scriptNamePath is a zero-length string');
			}

            if ($pathinfo === null) {
				throw new Core_ArgumentNullException('pathinfo is a null reference');
			}

            return self::getEngine()->parse($scriptNamePath, $pathinfo, $params);
        }

        /**
         * Escapes and simplifies a string to be a part of an url path
         * remove or replace not allowed characters etc..
         *
         * @param		string		$str : the string to escape
         * @param		boolean		$highlevel false : just to a urlencode. true, replace some characters
		 * @throws		Core_ArgumentException
		 * @throws		Core_ArgumentNullException
         * @return		string		escaped string
         */
        public static function escape($str, $highlevel = false)
        {
            if ($str === null) {
                throw new Core_ArgumentNullException('str is a null reference');
			}

            if (strlen(trim($str)) == 0) {
				throw new Core_ArgumentException('str is a zero-length string');
			}

            static $url_escape_to = null;
            static $url_escape_from = null;

            if ($highlevel) {
                if ($url_escape_from == null) {
                    $url_escape_to = explode(' ', 'a a a e e e e i i o o u u u A A A E E E E I I O O U U U c c');
                    $url_escape_from = explode(' ', 'à â ä é è ê ë ï î ô ö ù ü û À Â Ä É È Ê Ë Ï Î Ô Ö Ù Ü Û ç Ç');
                }

                // we don't use strtr because it is not utf8 compliant
                $str = str_replace($url_escape_from, $url_escape_to, $str);
                $str = preg_replace("/([^\w])/", ' ', $str);
                $str = preg_replace("/( +)/", '-', trim($str));
                $str = strtolower($str);

                return $str;
            } else {
                return urlencode(str_replace(array('-', ' '), array('--', '-'), $str));
            }
        }

        /**
         * Performs the opposit of escape
         *
         * @param		string		$str : the string to escape
		 * @throws		Core_ArgumentException
		 * @throws		Core_ArgumentNullException
         * @return		string
         */
        public static function unescape($str)
        {
            if ($str === null) {
                throw new Core_ArgumentNullException('str is a null reference');
			}

            if (strlen(trim($str)) == 0) {
				throw new Core_ArgumentException('str is a zero-length string');
			}

            return strtr($str, array('--' => '-', '-' => ' '));
        }

        /**
         * Gets the url corresponding to an action, in the given format
         *
         * @param        string      $selector : action selector.
         *                                       You can use # instead of the module
         *                                       or the action name, to specify the current url.
         * @param        array       $params : associative array with the parameters
         * @param        integer     $what : the format you want : STRING XMLSTRING URL URLACTION
		 * @throws		 Core_ArgumentException
		 * @throws		 Core_ArgumentNullException
         * @return       mixed       a value, depending of the $what parameter
         */
        public static function get($selector, array $params = array(), $what = Core_Url::STRING)
        {
            if ($selector === null) {
				throw new Core_ArgumentNullException('selector is a null reference');
			}

            if (strlen(trim($selector)) == 0) {
				throw new Core_ArgumentException('selector is a zero-length string');
			}

            $sel = new Core_Selector_GenericAction($selector, true);

            $params['module'] = $sel->getModule();
            $params['action'] = $sel->getResource();
            $action = new Core_Url_Action($params, $sel->getRequestType());

            if ($what == Core_Url::URLACTION) {
                return $action;
            }

            $url = self::getEngine()->create($action);

            if ($what == Core_Url::URL) {
                return $url;
            }

            return $url->_toString($what != Core_Url::STRING);
        }
    }
